//
// Created by suzhen on 17-3-20.
//

#ifndef ORIONV2_TRAITS_H
#define ORIONV2_TRAITS_H

#include <type_traits>

namespace TypeTraits {
    
    template<bool,
            typename,
            typename>
    struct TypeSelector {
    };
    
    template<typename T, typename T2>
    struct TypeSelector<true, T, T2> {
        using type = T;
    };
    
    template<typename T, typename T2>
    struct TypeSelector<false, T, T2> {
        using type = T2;
    };
    
    template<typename>
    struct ExtractMemberFunction;
    
    template<typename Ret, typename T>
    struct ExtractMemberFunction<Ret (T::*)(void)> {
        using Rt = Ret;
        using FirstArg = void;
        constexpr static bool void_param = true;
    };
    
    
    template<typename Ret, typename T, typename First, typename... Args>
    struct ExtractMemberFunction<Ret (T::*)(First, Args...)> {
        using Rt = Ret;
        using FirstArg = First;
        constexpr static bool void_param = false;
    };
    
    template<typename Ret, typename T, typename First, typename Second, typename... Args>
    struct ExtractMemberFunction<Ret (T::*)(First, Second, Args...)> {
        using Rt = Ret;
        using FirstArg = First;
        using SecondArg = Second;
        constexpr static bool void_param = false;
    };
    
    //
    
    template<typename Ret, typename T>
    struct ExtractMemberFunction<Ret (T::*)(void) const> {
        using Rt = Ret;
        using FirstArg = void;
        constexpr static bool void_param = true;
    };
    
    template<typename Ret, typename T, typename First, typename... Args>
    struct ExtractMemberFunction<Ret (T::*)(First, Args...) const> {
        using Rt = Ret;
        using FirstArg = First;
        constexpr static bool void_param = false;
    };
    
    template<typename Ret, typename T, typename First, typename Second, typename... Args>
    struct ExtractMemberFunction<Ret (T::*)(First, Second, Args...) const> {
        using Rt = Ret;
        using FirstArg = First;
        using SecondArg = Second;
        constexpr static bool void_param = false;
        
    };
    
    template<typename T>
    struct IsCallable {
        
        template<typename U, U>
        struct OperatorCall;
        
        template<typename>
        static int test(...);
        
        template<typename Tp>
        static char test(OperatorCall<void (Tp::*)(), &Tp::operator()> *);
        
        template<typename Tp>
        static char test(OperatorCall<void (Tp::*)() const, &Tp::operator()> *);
        
        static constexpr bool value = sizeof(test<T>(0)) == 1;
    };
    
}

#endif //ORIONV2_TRAITS_H
